/**
 * axios 接口封装
 * 版本：0.18.1
 * 功能：
 *    重发处理
 *    拦截处理
 * 注意：
 *    接口调用自己 try catch
 */
import axios from 'axios'

const _axios = axios.create()

// 基础配置
_axios.defaults.timeout = 5 * 1000        // 请求超时
_axios.defaults.retry = 2                 // 超时重发次数
_axios.defaults.retryDelay = 1 * 1000     // 超时重发间隔
_axios.defaults.withCredentials = false   // 请求头cookie携带
_axios.defaults.crossDomain = true        // 跨域配置,本地开发仍需要做代理处理
_axios.defaults.validateStatus = (status) => {
  return status >= 200 && status < 510
}

// 根据NODE_ENV配置设置不同环境的baseURL
if (process.env.NODE_ENV == 'development') {    
  _axios.defaults.baseURL = ''
}else if (process.env.NODE_ENV == 'debug') {    
  _axios.defaults.baseURL = ''
}else if (process.env.NODE_ENV == 'production') {    
  _axios.defaults.baseURL = ''
}

// 请求拦截器
_axios.interceptors.request.use(
  config => {
    const reqConfig = { ...config }

    // 配置参数 容错处理 
    if (!reqConfig.url) {
      // 提示
      console.error('request need url')
      throw new Error({
        source: 'axiosInterceptors',
        message: '请求地址为空',
      })
    }

    if (!reqConfig.method) {
      // 默认使用 get 请求
      reqConfig.method = 'get'
    }

    // 大小写容错
    reqConfig.method = reqConfig.method.toLowerCase()

    // 参数容错
    if (reqConfig.method === 'get') {
      if (!reqConfig.params) {
        // 防止字段用错
        reqConfig.params = reqConfig.data || {}
      }
    } else if (reqConfig.method === 'post') {
      if (!reqConfig.data) {
        // 防止字段用错
        reqConfig.data = reqConfig.params || {}
      }

      // 检测是否包含文件类型, 若包含则进行 formData 封装
      let hasFile = false
      Object.keys(reqConfig.data).forEach(key => {
        if (typeof reqConfig.data[key] === 'object') {
          const item = reqConfig.data[key]
          if (item instanceof FileList || item instanceof File || item instanceof Blob) {
            hasFile = true
          }
        }
      })

      // 检测到存在文件使用 FormData 提交数据
      if (hasFile) {
        const formData = new FormData()
        Object.keys(reqConfig.data).forEach(key => {
          formData.append(key, reqConfig.data[key])
        })
        reqConfig.data = formData
      }
    } else {
      // TODO: 其他类型请求数据格式处理
      console.warn(`其他请求类型: ${reqConfig.method}, 暂无自动处理`)
    }

    // auth 处理 请求头加token、cookieid
    // TODO

    // 如 双令牌刷新机制
    if (reqConfig.url === 'api/v1/user/refresh') {
      // const refreshToken = getToken('refresh_token')
      const refreshToken =  undefined
      if (refreshToken) {
        reqConfig.headers.Authorization = refreshToken
      }
    } else {
      // access_token
      // const accessToken = getToken('access_token')
      const accessToken =  undefined
      if (accessToken) {
        reqConfig.headers.Authorization = accessToken
      }
    }

    return reqConfig
  },
  error => {
    return Promise.reject(error)
  }
)

// 请求超时重发处理
function axiosTimeOutRetry(error) {
  const config = error.config

  if(!config || !config.retry) {
    return Promise.reject(error)
  }
  
  config.__retryCount = config.__retryCount || 0
  
  if(config.__retryCount >= config.retry) {
      return Promise.reject(error)
  }
  
  config.__retryCount += 1;
  
  var backoff = new Promise(function(resolve) {
      setTimeout(function() {
          resolve()
      }, config.retryDelay || 1)
  })
  
  return backoff.then(function() {
      return _axios(config)
  })
}

// 响应拦截
_axios.interceptors.response.use(
  res => {
    if (res.status.toString().charAt(0) === '2') {    
      return res.data
    }

    return new Promise(async (resolve, reject) => {
      // status != 2xxx
      // 异常处理逻辑 自己实现：重定向,重发,转发,提示
      // TODO

      let { errorCode, msg } = res.data // 后端接口返回的json结构体
      const { params, url } = res.config

      // 如 特定状态码 处理

      // 双令牌的机制业务
      // refresh_token 异常，直接登出
      if (errorCode === 10101 || errorCode === 10102) {
        setTimeout(() => {
          store.dispatch('loginOut')
          const { origin } = window.location
          window.location.href = origin
        }, 1500)
        resolve(null)
        return
      }

      // 双令牌的刷新令牌操作(重发处理逻辑)
      // 实现类似无感知令牌刷新, 让请求正常响应
      if (errorCode === 19001) {
        const cache = {}
        if (cache.url !== url) {
          const refreshResult = await _axios('api/v1/user/refresh')
          // save token 处理
          // 用新的token, 将上次失败请求重发
          const result = await _axios(res.config)
          resolve(result)
          return
        }
      }

      // 弹出提示处理
      // TODO
      alert('弹出提示')

      reject()
    })
  },
  error => {
    if (!error.response) {
      console.error(error)
    }
    // 判断请求超时
    if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
      console.warn('请求超时 重分处理')
      axiosTimeOutRetry(error)
    }
    return Promise.reject(error)
  }
)

// vue plugin扩展
// 注入vue this属性里
import Vue from 'vue'
Plugin.install = function(Vue, options) {
  Vue.axios = _axios
  window.axios = _axios
  Object.defineProperties(Vue.prototype, {
    axios: {
      get() {
        return _axios
      },
    },
    $axios: {
      get() {
        return _axios
      },
    },
  })
}
if (!Vue.axios) {
  Vue.use(Plugin)
}

// 导出常用函数

/**
 * get请求
 * @param {string} url 地址
 * @param {object} params params参数
 */
export function get(url, params = {}) {
  return  _axios({
    method: 'get',
    url,
    params
  })
}

/**
 * post请求
 * @param {string} url 地址
 * @param {object} data data参数
 * @param {object} params params参数
 */
export function post(url, data = {}, params = {}) {
  return _axios({
    method: 'post',
    url,
    data,
    params
  })
}

/**
 * put请求
 * @param {string} url 地址
 * @param {object} data data参数
 * @param {object} params params参数
 */
export function put(url, data = {}, params = {}) {
  return _axios({
    method: 'put',
    url,
    params,
    data
  })
}

/**
 * delete请求
 * @param {string} url 地址
 * @param {object} params 删除对象
 */
export function _delete(url, params = {}) {
  return _axios({
    method: 'delete',
    url,
    params
  })
}
